package cn.caplike.demo.repository.design.pattern.combined.annotation;

import cn.caplike.demo.repository.design.pattern.combined.annotation.definition.ApiDataAdapter;
import cn.caplike.demo.repository.design.pattern.combined.annotation.definition.FinanceType;
import cn.caplike.demo.repository.design.pattern.combined.annotation.definition.FinanceVersion;
import cn.caplike.demo.repository.design.pattern.combined.annotation.dto.Business;
import cn.caplike.demo.repository.design.pattern.combined.annotation.dto.RequestBusinessData;
import cn.caplike.demo.repository.design.pattern.combined.annotation.dto.RequestListData;
import cn.caplike.demo.repository.design.pattern.combined.annotation.dto.RequestOutlayData;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.NameFilter;
import com.alibaba.fastjson.serializer.PropertyFilter;
import com.alibaba.fastjson.serializer.SerializeFilter;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class App {

    public static void main(String[] args) {

        // ~ 缓存
        ApiCache.Builder.of().financeType(FinanceType.TY).financeVersion(FinanceVersion.v20190401_TY);

        // ~ 初始化对象

        final RequestBusinessData businessData = RequestBusinessData.builder().bpmNo("bpmNo").build();

        final String firstId = UUID.randomUUID().toString();
        final String secondId = UUID.randomUUID().toString();
        final String thirdId = UUID.randomUUID().toString();

        final List<RequestOutlayData> requestOutlayDataList = Stream.of(
                RequestOutlayData.builder().bpmNo(firstId).build(),
                RequestOutlayData.builder().bpmNo(secondId).build(),
                RequestOutlayData.builder().bpmNo(thirdId).build()
        ).collect(Collectors.toList());

        final List<RequestListData> requestListDataList = Stream.of(
                RequestListData.builder().bpmNo(firstId).build(),
                RequestListData.builder().bpmNo(secondId).build(),
                RequestListData.builder().bpmNo(thirdId).build()
        ).collect(Collectors.toList());

        final Business business = Business.builder().businessData(businessData).outlayData(requestOutlayDataList).requestListData(requestListDataList).build();

        final String json = Wrapper.to(business);
        System.out.println("json: " + json);

        final List<Business> parse = Wrapper.parse(json, Business.class);
        System.out.println("data: " + parse.get(0));
    }

    /**
     * 包装器
     *
     * @author LiKe
     * @date 2019-11-03 11:12
     */
    private static final class Wrapper {

        /**
         * Description: 解析业务系统 Bean, 转换成 finance-middleware 可读的 Bean (JSON)
         *
         * @param clientData 客户端数据
         * @return JSON 数据
         * @author LiKe
         * @date 2019-11-03 11:14:08
         */
        public static <GenericData> String to(GenericData clientData) {
            final String financeType = ApiCache.get(ApiCache.Key.FINANCE_TYPE), financeVersion = ApiCache.get(ApiCache.Key.FINANCE_VERSION);
            return JSON.toJSONString(
                    clientData,
                    new SerializeFilter[]{
                            (NameFilter) (object, name, value) -> {
                                try {
                                    Field field = object.getClass().getDeclaredField(name);
                                    for (ApiDataAdapter.Finance finance : field.getAnnotation(ApiDataAdapter.class).value()) {
                                        if (financeType.equals(finance.type().name()) && (financeVersion + "_" + financeType).equals(finance.version().name())) {
                                            return finance.fieldName();
                                        }
                                    }
                                } catch (NoSuchFieldException ignored) {
                                }
                                return name;
                            },
                            (PropertyFilter) (object, name, value) -> {
                                try {
                                    // 没有加注解，不做映射
                                    if (Objects.isNull(object.getClass().getDeclaredField(name).getAnnotation(ApiDataAdapter.class)))
                                        return false;
                                } catch (NoSuchFieldException ignored) {
                                }
                                return true;
                            }
                    }
            );
        }

        /**
         * Description: 把服务端返回 JSON 数据解析成对客户端友好的 JAVA 对象<br>
         * NecessaryCondition: 如果目标类实现了 {@link cn.caplike.demo.repository.design.pattern.combined.annotation.definition.DeserializeDescriptor}
         * 并且目标类的 field 标注了 {@link ApiDataAdapter}
         *
         * @param jsonStr 服务端返回 JSON 数据
         * @param clazz   客户端友好的 JAVA 对象的 CLASS 对象,
         *                <strong>需要实现 {@link cn.caplike.demo.repository.design.pattern.combined.annotation.definition.DeserializeDescriptor}
         *                超类</strong>
         * @return GenericClientObject 客户端友好的 JAVA 对象
         * @author LiKe
         * @date 2019-11-06 09:32:22
         */
        @SuppressWarnings("unchecked")
        public static <GenericClientObject> List<GenericClientObject> parse(String jsonStr, Class<GenericClientObject> clazz) {
            if (String.class.equals(clazz))
                return (List<GenericClientObject>) Collections.singletonList(jsonStr);

            Object any = JSON.parse(jsonStr);
            if (any instanceof JSONObject)
                return Collections.singletonList(JSON.parseObject(jsonStr, clazz));
            if (any instanceof JSONArray)
                return JSON.parseArray(jsonStr, clazz);
            throw new RuntimeException("未能识别的类型: " + any.getClass().getCanonicalName());
        }
    }
}
